package com.uft.service;

import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

import org.apache.commons.lang3.StringUtils;
import org.apache.log4j.Logger;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.AsyncResult;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import com.ibm.websphere.management.AdminClient;
import com.uft.model.dao.AuditDAO;
import com.uft.model.dao.PreferencesDAO;
import com.uft.model.dao.ProfileDAO;
import com.uft.model.dao.ServerDAO;
import com.uft.model.entity.Preferences;
import com.uft.model.entity.Profile;
import com.uft.model.entity.ProfilePreferences;
import com.uft.model.entity.Server;
import com.uft.utils.JDOMUtil;
import com.uft.utils.UftUtils;
import com.uft.was.app.WasAppManager;
import com.uft.was.config.Xml;
import com.uft.was.connector.WasConnectorManager;

@Service
public class WasService{

	@Autowired
	ProfileDAO profileDAO;
	@Autowired
	ServerDAO serverDAO;
	@Autowired
	PreferencesDAO preferencesDAO;
	@Autowired
	private  AuditDAO auditDAO;
	@Autowired
	private  TaskTracker taskTracker;
	private  static Logger  logger = Logger.getLogger(WasService.class);
	
	@Value("${wasServices.serverStartWaitTimeout:120}") private  int SERVER_START_TIMEOUT;
	@Value("${wasServices.soapTimeOut:1800}") private int SOAP_TIMEOUT;
	@Value("${wasServices.connection.retry:1800}") private int CONNECTION_RETRY;
	@Value("${wasServices.conection.server.startWaitTime:119}") private int START_WAIT_TIME;
	@Value("${wasServices.conection.server.stopWaitRetry:4}") private int STOP_WAIT_RETRY;
	@Value("${wasServices.conection.server.stopWaitTime:10}") private int STOP_WAIT_TIME;	
	private static Hashtable<String, WasConnectorManager> wasConnectorManagerTable= new Hashtable<String, WasConnectorManager>();
	
	
	
	public WasConnectorManager initializeWasConnectorMgr(String profileId){
		WasConnectorManager wasConnectorMgr= new WasConnectorManager();
		Profile profile=profileDAO.getProfileById(Integer.parseInt(profileId));
		ProfilePreferences profilePref=profile.getProfilePref();
		
		// get SOAP CONFIG FILE
		wasConnectorMgr.setProfileName(profile.getProfileName());
		wasConnectorMgr.setRegion(profile.getRegion());
		if(StringUtils.isNotBlank(profilePref.getSoapConfig())){
			
			wasConnectorMgr.setSoapCfgURL(profilePref.getSoapConfig());
			logger.debug("Got the SOAP CONFIG FILE from Local Profile ");
		}else{
			Preferences saopConfigPref=preferencesDAO.getPreferencesByCategoryName("SECURITY", "SOAP_CONFIG_PROP");
			if(saopConfigPref != null ){
				wasConnectorMgr.setSoapCfgURL(saopConfigPref.getPrefValue());
				logger.debug("Got the SOAP CONFIG FILE from Global Preference ");
			}
		}
		
		//get SSL Config FIle
		if(StringUtils.isNotBlank(profilePref.getSslConfig())){
			wasConnectorMgr.setSslCfgURL(profilePref.getSslConfig());
			logger.debug("Got the SSL CONFIG FILE from Local Profile ");
		}else{
			Preferences sslCfgURL=preferencesDAO.getPreferencesByCategoryName("SECURITY", "SSL_CONFIG_PROP");
			if(sslCfgURL != null  ){
				wasConnectorMgr.setSslCfgURL(sslCfgURL.getPrefValue());
				logger.debug("Got the SSL CONFIG FILE from Global Preference ");
			}
		}
		//get Trust Store
		if(StringUtils.isNotBlank(profilePref.getTrustStore())){
			wasConnectorMgr.setTrustStore(profilePref.getTrustStore());
			logger.debug("Got the TRUST STORE from Local Profile ");
		}else{
			Preferences trustStore=preferencesDAO.getPreferencesByCategoryName("SECURITY", "TRUST_STORE");
			if(trustStore != null ){
				wasConnectorMgr.setTrustStore(trustStore.getPrefValue());
				logger.debug("Got the TRUST STORE from Global Preference ");
			}
		}
		//get KEY Store
		if(StringUtils.isNotBlank(profilePref.getKeyStore())){
			wasConnectorMgr.setKeyStore(profilePref.getKeyStore());
			logger.debug("Got the KEY STORE from Local Profile ");
		}else{
			Preferences keyStore=preferencesDAO.getPreferencesByCategoryName("SECURITY", "KEY_STORE");
			if(keyStore != null ){
				wasConnectorMgr.setKeyStore(keyStore.getPrefValue());
				logger.debug("Got the KEY STORE from Global Preference ");
			}
		}
		//get TRUST PASSWORD
		if(StringUtils.isNotBlank(profilePref.getTrustPassword())){
			wasConnectorMgr.setTrustPassword(profilePref.getTrustPassword());
			logger.debug("Got the TRUST PASSWORD from Local Profile ");
		}else{
			Preferences trustPassword=preferencesDAO.getPreferencesByCategoryName("SECURITY", "TRUST_STORE_PASSWORD");
			if(trustPassword != null ){
				wasConnectorMgr.setTrustPassword(trustPassword.getPrefValue());
				logger.debug("Got the TRUST PASSWORD from Global Preference ");
			}
		}
		//get KEY PASSWORD
		if(StringUtils.isNotBlank(profilePref.getKeyPassword())){
			wasConnectorMgr.setKeyPassword(profilePref.getKeyPassword());
			logger.debug("Got the KEY PASSWORD from Local Profile ");
		}else{
			Preferences keyPassword=preferencesDAO.getPreferencesByCategoryName("SECURITY", "KEY_STORE_PASSWORD");
			if(keyPassword != null ){
				wasConnectorMgr.setKeyPassword(keyPassword.getPrefValue());
				logger.debug("Got the KEY PASSWORD from Global Preference ");
			}
		}
		
		Preferences wasNotification=preferencesDAO.getPreferencesByCategoryName("ADMINISTRATION", "DISABLE_WAS_NOTIFICATION");
		if(wasNotification != null ){
			if(wasNotification.getPrefValue().equalsIgnoreCase("true")){
				wasConnectorMgr.setRegisterListener(false);
			}
			
		}
		Preferences wasInstallPath=preferencesDAO.getPreferencesByCategoryName("ADMINISTRATION", "WAS_INSTALL_PATH");
		if(wasInstallPath != null ){
			if(!wasInstallPath.getPrefValue().equalsIgnoreCase("")){
				wasConnectorMgr.setWasInstallPath(wasInstallPath.getPrefValue());
			}
			
		}
		Preferences wasWsadminClasspath=preferencesDAO.getPreferencesByCategoryName("DEPLOYMENT", "WAS_WSADMIN_CLASSPATH");
		if(wasWsadminClasspath != null ){
			if(!wasWsadminClasspath.getPrefValue().equalsIgnoreCase("")){
				wasConnectorMgr.setWsadminClasspath(wasWsadminClasspath.getPrefValue());
			}
			
		}
		if(StringUtils.isNotBlank(profilePref.getConfigScript())){
			
			wasConnectorMgr.setConfigScript(profilePref.getConfigScript());
			logger.debug("Adding Configuration Script from Local Profile ");
		}
		wasConnectorMgr.setHostname(profile.getHostname());
		wasConnectorMgr.setPort(String.valueOf(profile.getPort()));
		wasConnectorMgr.setSecurity(Boolean.valueOf(profile.getSecurity()));
		wasConnectorMgr.setUsername(profile.getUsername());
		wasConnectorMgr.setPassword(profile.getPassword());
		return wasConnectorMgr;
		
	}
	public boolean addWasAdminClient(String profileId){
		logger.debug("Size of cache:"+wasConnectorManagerTable.size());
		if(wasConnectorManagerTable.containsKey(profileId)){
			if( !removeWasAdminClient(profileId)){
				logger.debug("Unable to Remove Existing Connector from Cache: "+profileId);
				return false;
			}
		}
		WasConnectorManager wasConnectorMgr=initializeWasConnectorMgr(profileId);
		wasConnectorMgr.initialiazeConnection();
		try{
			logger.debug("Adding to cache: "+profileId);
			wasConnectorManagerTable.put(profileId, wasConnectorMgr);	
			logger.debug("Size after adding cache:"+wasConnectorManagerTable.size());
			return true;
		}catch(Exception e){
			logger.error(e.getMessage(), e);
			return false;
		}
		
		
		
	}
	
	public boolean testWasAdminClientConnection(String profileId){
		WasConnectorManager wasConnectorMgr=initializeWasConnectorMgr(profileId);	
		return wasConnectorMgr.testConnectionOnly();
	}
	
	public boolean removeWasAdminClient(String profileId){	
		logger.debug("Removing..."+profileId);
		logger.debug("Size: "+wasConnectorManagerTable.size());
		if(wasConnectorManagerTable.containsKey(profileId)){
			logger.debug("Found the ..."+profileId);
			
			WasConnectorManager wasConnectorMgr=wasConnectorManagerTable.remove(profileId);
			wasConnectorMgr=null;
			logger.debug("removed..."+profileId);
			logger.debug("Now the Size: "+wasConnectorManagerTable.size());
			return true;
		}
		return false;
	}
	
	public AdminClient getWasAdminClient(String profileId){
		if(wasConnectorManagerTable.containsKey(profileId)){
			return wasConnectorManagerTable.get(profileId).getAdminClient();
		}
		return null;
	}
	

	public String serverStatus(String profileId, String serverId) {
		// TODO Auto-generated method stub
		String result="";
		if(!wasConnectorManagerTable.containsKey(profileId)){
			logger.debug("create new Connection for profileId: "+profileId);
			logger.debug("Adding: "+addWasAdminClient(profileId));
		}
		WasConnectorManager wasConMgr=wasConnectorManagerTable.get(profileId);
		if(!wasConMgr.isConnected()){
			logger.debug("Connection is not active: Retrying "+CONNECTION_RETRY);
			wasConMgr.retryConnection(CONNECTION_RETRY);
			if(!wasConMgr.isConnected()){
				result="Connection Retries reached.. Connection Failed. Unabel to get Status";
			}
		}
		if(wasConMgr.isConnected()){
			logger.debug("Connection Available "+profileId);
			Server server=serverDAO.getServerById(Integer.parseInt(serverId));
			String nodeName=server.getNodeName();
			String serverName=server.getServerName();
			logger.debug("Server Name: "+serverName+" nodeName: "+nodeName);
			result=wasConMgr.getServerStatus(nodeName, serverName);
			serverDAO.updateServerStatus(serverId, result);
			
		}else{
			result= "Not Connected to DMGR Process";
		}
		
		return result;
	}
	@Async
	public Future<Object> updateServerList(String profileId){
		String result="";
		if(!wasConnectorManagerTable.containsKey(profileId)){
			logger.debug("create new Connection for profileId: "+profileId);
			addWasAdminClient(profileId);
		}
		logger.debug("Size of the Cache: "+wasConnectorManagerTable.size());
		logger.debug("keys: "+wasConnectorManagerTable.keySet().toString());
		if(wasConnectorManagerTable.containsKey(profileId)){
			List<HashMap<String,String>> serverList=wasConnectorManagerTable.get(profileId).getAllServers();
			//Profile profile=profileDAO.getProfileById(Integer.parseInt(profileId));
			logger.debug("Got the List: serverList size "+serverList.size());
			for(HashMap<String,String> serverHashMap:serverList){
				String serverName=serverHashMap.get("serverName");
				String nodeName=serverHashMap.get("nodeName");
				String serverHostname=serverHashMap.get("nodeName");
				List<Server> sList= serverDAO.getServerByNameNodeHost(serverName, nodeName, serverHostname);
				if(sList.isEmpty()){
					logger.debug("Server :"+serverName+" Does't exists...Addiing new");
					Server server= new Server();
					server.setServerName(serverName);
					server.setNodeName(nodeName);
					server.setServerHostname(serverHostname);
					server.setNodeHostname(serverHostname);
					logger.debug(server.toString());
					serverDAO.addNewServerByProfileId(profileId, server);
				}
				
			}
			result="Update Server Task Complete";
		}else{
			result="AdminClient Cache Object is not available";
		}
		logger.debug("profileId: "+profileId+" : "+result);
		return new AsyncResult<Object>(result);
		
	}
	@Async
	public Future<Object> addStartServerTask(String profileId,String serverId) {
		String result="";
		
		if(!wasConnectorManagerTable.containsKey(profileId)){
			logger.debug("create new Connection for profileId: "+profileId);
			addWasAdminClient(profileId);
		}
		WasConnectorManager wasConMgr=wasConnectorManagerTable.get(profileId);
		if(!wasConMgr.isConnected()){
			logger.debug("Connection is not active: Retrying "+CONNECTION_RETRY);
			wasConMgr.retryConnection(CONNECTION_RETRY);
			if(!wasConMgr.isConnected()){
				result="Connection Retries reached.. Connection Failed.";
			}
		}
		if(wasConMgr.isConnected()){
			logger.debug("Connection Available "+profileId);
			Server server=serverDAO.getServerById(Integer.parseInt(serverId));
			String nodeName=server.getNodeName();
			String serverName=server.getServerName();
			logger.debug("Server Name: "+serverName+" nodeName: "+nodeName);
			if(!wasConMgr.getServerStatus(nodeName, serverName).equalsIgnoreCase("RUNNING")){
				logger.debug("Server Name: "+serverName+" nodeName: "+nodeName+" Stopped.. Starting now");
				boolean  complete=wasConMgr.startServer(nodeName, serverName, START_WAIT_TIME);
				if(complete){
					result="STARTED";
					
				}else{
					result="Error occured while starting";
				}
			}else{
				
				result="Server: "+serverName+" is already Running";
			}
		}
		logger.debug("ServerId: "+serverId+" : "+result);
		return new AsyncResult<Object>(result);
	}
	@Async
	public Future<Object> addStopServerTask(String profileId,String serverId) {
		String result="";
		
		if(!wasConnectorManagerTable.containsKey(profileId)){
			logger.debug("create new Connection for profileId: "+profileId);
			logger.debug("Adding: "+addWasAdminClient(profileId));
		}
		WasConnectorManager wasConMgr=wasConnectorManagerTable.get(profileId);
		if(!wasConMgr.isConnected()){
			logger.debug("Connection is not active: Retrying "+CONNECTION_RETRY);
			wasConMgr.retryConnection(CONNECTION_RETRY);
			if(!wasConMgr.isConnected()){
				result="Connection Retries reached.. Connection Failed.";
			}
		}
		if(wasConMgr.isConnected()){
			logger.debug("Connection Available "+profileId);
			Server server=serverDAO.getServerById(Integer.parseInt(serverId));
			String nodeName=server.getNodeName();
			String serverName=server.getServerName();
			logger.debug("Server Name:  "+serverName+" , nodeName="+nodeName);
			if(!wasConMgr.getServerStatus(nodeName, serverName).equalsIgnoreCase("STOPPED")){
				boolean  complete=wasConMgr.stopServer(nodeName, serverName);
				if(complete){
					for (int i = 0; i < STOP_WAIT_RETRY;i++){
						String status=wasConMgr.getServerStatus(nodeName, serverName);
						if(status.equalsIgnoreCase("STOPPED")){
							result="STOPPED";
							break;
						}else{
							try {
								Thread.sleep(STOP_WAIT_TIME*1000);
							} catch (InterruptedException e) {
								// TODO Auto-generated catch block
								e.printStackTrace();
							}
						}
					}
					if(result.equalsIgnoreCase("")){
						result="Server did't stop in time. Check Server logs";
					}
					
				}else{
					result="Error occured while stopping";
				}
			}else{
				result="Server: "+serverName+" is already STOPPED";
			}
		}
		logger.debug("ServerId: "+serverId+" ,"+result);
		return new AsyncResult<Object>(result);
	}
	
	public List<HashMap<String, String>> getWasServiceStats(){
		List<HashMap<String,String>> rList= new ArrayList<HashMap<String,String>>();
		Iterator keys=wasConnectorManagerTable.keySet().iterator();
		while(keys.hasNext()){
			String id=(String)keys.next();
			WasConnectorManager value=wasConnectorManagerTable.get(id);
			HashMap<String, String> temp = new HashMap<String,String>();
			temp.put("profileId", id);
			temp.put("profileName", value.getProfileName());
			temp.put("username", value.getUsername());
			temp.put("region", value.getRegion());
			temp.put("dateStarted", value.getInitDate().toString());
			temp.put("status",String.valueOf(value.checkConnected()));
			rList.add(temp);
			
		}
		
		return rList;
	}
	
	public Xml getRuntimeConfig(String profileId, String nodeName, String serverName, String clusterName) throws Exception{
		if(!wasConnectorManagerTable.containsKey(profileId)){
			logger.debug("create new Connection for profileId: "+profileId);
			logger.debug("Adding: "+addWasAdminClient(profileId));
		}
		WasConnectorManager wasConMgr=wasConnectorManagerTable.get(profileId);
		return wasConMgr.retrieveServerCfgInfo(nodeName, serverName, clusterName);
		
	}
	
	public String updatWasConfiguration(String profileId,String task,String xml, String xpathValue){
		Document doc= JDOMUtil.stringToDocument(xml);
		String parms="-a,update,";
		parms+="-t,"+task +",";
		List<Element> clusterNodeList;
		String results="";
		logger.debug(parms);
		try {
			clusterNodeList = JDOMUtil.getElementByXpathNameSpace(doc, "/ns:xml/ns:Cluster/ns:clusterNode");
			if(clusterNodeList.size() == 0){
				return "NodeName and ServerName is empty";
			}
			if(!wasConnectorManagerTable.containsKey(profileId)){
				logger.debug("create new Connection for profileId: "+profileId);
				logger.debug("Adding: "+addWasAdminClient(profileId));
			}
			WasConnectorManager wasConMgr=wasConnectorManagerTable.get(profileId);
			if(!wasConMgr.isConnected()){
				logger.debug("Connection is not active: Retrying "+CONNECTION_RETRY);
				wasConMgr.retryConnection(CONNECTION_RETRY);
				if(!wasConMgr.isConnected()){
					return "Connection Retries reached.. Connection Failed.";
				}
			}
			for (Iterator<Element> itr = clusterNodeList.iterator(); itr.hasNext(); ) {
				Element item = itr.next();
				String nodeName=item.getChildText("nodeName", item.getNamespace());
				String serverName=item.getChildText("serverName", item.getNamespace());
				List<Element> eleList=JDOMUtil.getElementByXpathNameSpace(doc, xpathValue);
				if(eleList.size() >= 1){
					Element ele= eleList.get(0);
					if (ele.getChildren().size() == 0){
						String key=ele.getName();
						String value=ele.getText();
						if(wasConMgr.isConnected()){
							logger.debug("Connection Available "+profileId);
							int eCode=wasConMgr.runScript(wasConMgr.getConfigScript(), parms+"-n,"+nodeName+",-s,"+serverName+",-k,"+key+",-v,"+value);
							if(eCode == 0 || eCode == 86){
								results="Success";
							}else{
								results="Error Occuried while updating..";
							}
							
						}
					}else{
						String xmlEle=JDOMUtil.elementToStringDump(ele);
						if(wasConMgr.isConnected()){
							logger.debug("Connection Available "+profileId);
							int eCode=wasConMgr.runScript(wasConMgr.getConfigScript(), parms+"-n,"+nodeName+",-s,"+serverName+",-x, "+xmlEle);
							if(eCode == 0 || eCode == 86){
								results="Success";
							}else{
								results="Error Occuried while updating..";
							}
							
						}
						
					}
				}else{
					results="No Element found:";
				}
					
				
			}
		} catch (JDOMException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		
		
		return results;
	}
	
}
